#ifndef _SCENE_H_
#define _SCENE_H_

#include <SECore/Ecs/Actor.h>
#include <vector>
#include <functional>
#include <SECore/Util.h>
#include <variant>
#include <SECore/Serializable.h>

namespace SEngine
{
    class Scene
    {
    public:
        ENABLE_SERIALIZE(Scene)
    public:
        Scene() {};
        Scene(const Scene &) {}
        virtual ~Scene() = default;
        

        Actor & CreateActor();
        void DestroyActor(Actor & actor);
        void OnBeforeUpdate();
        void OnUpdate(int delta);
        void OnAfterUpdate();
        void ForeachActor(std::function<void(Actor &)> && fn);
        template<typename... Ts, typename F>
        auto Components(F && fn) 
            -> std::enable_if_t<std::is_void_v<std::invoke_result_t<F, Actor&, Ts&...>>>
        {
            auto actorsCopy = m_actors;
            if constexpr (sizeof...(Ts) > 1) {
                for (auto & actor : actorsCopy) {
                    if (IsAllTrue(actor->HasComponent<Ts>()...)) {
                        fn(*actor, actor->GetComponent<Ts>()->get()...);
                    }
                }
            } else {
                Component<Ts...>(std::forward<decltype(fn)>(fn));
            }
        }
        template<typename... Ts, typename F>
        auto Components(F && fn) 
            -> std::enable_if_t<std::is_same_v<std::invoke_result_t<F, Actor&, Ts&...>, bool>>
        {
            auto actorsCopy = m_actors;
            if constexpr (sizeof...(Ts) > 1) {
                for (auto & actor : actorsCopy) {
                    if (IsAllTrue(actor->HasComponent<Ts>()...)) {
                        if (!fn(*actor, actor->GetComponent<Ts>()->get()...)) {
                            break;
                        }
                    }
                }
            } else {
                Component<Ts...>(std::forward<decltype(fn)>(fn));
            }
        }
        template<typename F, typename T, typename... Ts>
        void ComponentsEx(F && fn, TypesLoader<T, Ts...> loader);

    protected:
        template<typename T, typename F>
        auto Component(F && fn) 
            -> std::enable_if_t<std::is_void_v<std::invoke_result_t<F, Actor&, T&>>>
        {
            auto id = typeid(T).hash_code();
            if (m_componts.find(id) == m_componts.end()) {
                return;
            }
            auto compontCopy = m_componts.at(id);
            for (auto pcomp : compontCopy) {
                fn(pcomp->m_actor, dynamic_cast<T&>(*pcomp));
            }
        }
        template<typename T, typename F>
        auto Component(F && fn) 
            -> std::enable_if_t<std::is_same_v<std::invoke_result_t<F, Actor&, T&>, bool>>
        {
            auto id = typeid(T).hash_code();
            if (m_componts.find(id) == m_componts.end()) {
                return;
            }
            auto compontCopy = m_componts.at(id);
            for (auto pcomp : compontCopy) {
                if (!fn(pcomp->m_actor, dynamic_cast<T&>(*pcomp))) {
                    break;
                }
            }
        }
        void AddComponentSlot(IComponent&);
        void RemoveComponentSlot(IComponent&);

    public: 
        Signal<IComponent&> AddComponentSignal;
        Signal<IComponent&> RemoveComponentSignal;

    private:
        std::vector<std::shared_ptr<Actor>> m_actors;
        std::unordered_map<size_t, std::vector<IComponent*>> m_componts;
    };


    template <typename F, typename T, typename... Ts>
    inline void Scene::ComponentsEx(F &&fn, TypesLoader<T, Ts...> loader)
    {
        auto id = typeid(T).hash_code();
        if (m_componts.find(id) == m_componts.end()) {
            return;
        }
        auto compontCopy = m_componts.at(id);
        for (auto pcomp : compontCopy) {
            fn(pcomp->m_actor, dynamic_cast<T&>(*pcomp));
        }
        if constexpr (sizeof...(Ts) > 0) {
            TypesLoader<Ts...> nextLoader;
            ComponentsEx<F, Ts...>(std::forward<F>(fn), nextLoader);
        }
    }
} // namespace SEngine

#endif